package mudus;

import fsoperation.FileUtil;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
import mudus.model.UserModel;

public class ConnectionHandler implements Runnable {

    Connection dbcon;        
    
    Gateway gateway;

    Socket socket;

    BufferedOutputStream out;

    BufferedInputStream in;

    boolean close;
    
    UserInfo userInfo;

    public ConnectionHandler(Gateway gateway, Socket connection) {
        this.socket = connection;
        this.gateway = gateway;

        try {
            out = new BufferedOutputStream(connection.getOutputStream());
            out.flush();
            in = new BufferedInputStream(connection.getInputStream());
        } catch (IOException ex) {
            Logger.getLogger(ConnectionHandler.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    
    //mengembalikan true jika user dan password benar
    protected boolean listenUsrPwd() throws IOException, ClassNotFoundException, SQLException {
        byte[] bmsg = new byte[1000];
        int bmsglen = in.read(bmsg);
        String message = new String(bmsg, 0, bmsglen);
        String[] data = message.split(" ");        
        String username = data[0];
        String pwdHash = data[1];//pwd di-hash dengan md5;
        startDBConnection();
        UserModel userModel = new UserModel(dbcon);
        ResultSet res = userModel.getUsrPwd(username, pwdHash);
        while(res.next()) {
            userInfo = new UserInfo(res.getInt("id"), res.getString("username"));
            return true;
        }
        return false;
    }
    
    protected void listenMsg() throws IOException {
        int btype = in.read();
        System.out.println("menerima tipe msg : " + btype);        
        switch(btype) {
            case GlobalSettings.DISCONNECT :
                close = true;
                break;
            case GlobalSettings.REQUEST_METAFILE :                 
                listenRequestMetafile();
                break;            
            case GlobalSettings.START_UPLOAD_FILE : 
                listenUploadFile();
                break;      
            case GlobalSettings.REQUEST_METALOGFILE :
                replyMetaLogFile();                
                break;            
            case GlobalSettings.REQUEST_DELETE_ON_SERVER : 
                deleteFile();
                break;
            case -1 : 
                close = true;
                break;
        }
    }
    
    protected void deleteFile() {
        //String message = 
    }
    
    protected void listenRequestMetafile() throws IOException {
        byte[] bmsg = new byte[1000];
        int bmsglen = in.read(bmsg);
        String message = new String(bmsg, 0, bmsglen);                
        String data[] = message.split("\\|");
        String filepath = data[0];
        String filename = data[1];        
        String pieceRequestMsg;
        int ipiece;
        int mtype;
        System.out.println("menerima request metafile > filepath : " + filepath + "  filename : " + filename);
        try {
            File file = FileUtil.getFile(userInfo, filepath, filename);
            byte[] response = Message.createMetaFileMsg(file);
            sendMsg(out, response);
            
            RandomAccessFile raf = new RandomAccessFile(file,"r");
            
            do {          
                //listen pesan berikutnya
                mtype = in.read();
                System.out.println("mtype : " + mtype);
                if(mtype == GlobalSettings.END_REQUEST_FILEPIECE) {
                    //client sudah menerima file utuh
                    System.out.println("menerima END_REQUEST_FILEPIECE");
                    break; 
                } else if(mtype == GlobalSettings.REQUEST_FILEPIECE) {
                    pieceRequestMsg = listenRequestFilePiece();
                    String[] temp  = pieceRequestMsg.split("\\|");                                        
                    ipiece = Integer.valueOf(temp[2]);                        
                    // kirim piece ke-ipiece                     
                    long remaining = file.length() - (ipiece * GlobalSettings.PIECE_LENGTH);
                    int piecelen = (int) ((remaining > GlobalSettings.PIECE_LENGTH) ? GlobalSettings.PIECE_LENGTH : remaining); 
                    byte[] piece = new byte[piecelen];                                        
                    raf.readFully(piece);                    
                    sendMsg(out, Message.createFilePieceMsg(ipiece, piece));   
                    System.out.println("selesai mengirimkan piece ke-" + ipiece);
                }else if(mtype == -1){
                    break;
                }                                
            }while(true);             
        }catch(FileNotFoundException e) {
            System.err.println(e.getMessage());
        }
    }
    
    protected String listenRequestFilePiece() throws IOException {
        byte[] bmsg = new byte[GlobalSettings.PIECE_LENGTH];        
        int bmsglen = in.read(bmsg);
        String message = new String(bmsg, 0, bmsglen);        
        message = message.substring(0, bmsglen);      
        System.out.println("menerima pesan request file piece : " + message);
        return message;
    }
    
    protected void listenUploadFile() throws IOException {
        byte[] bmsg = new byte[GlobalSettings.PIECE_LENGTH];        
        int bmsglen = in.read(bmsg);
        String message = new String(bmsg, 0, bmsglen);        
        String data[] = message.split("\\|");
        String filepath = data[0];
        String filename = data[1];
        int piecelen = Integer.parseInt(data[3]);
        int npiece = Integer.parseInt(data[4]);
        int ipiece = 0;
        int mtype;
        
        System.out.println("pesan permintaan upload diterima : " + message);
        FileUtil.makeFile(userInfo, filepath, filename + ".TEMP");
        sendMsg(out, Message.createUploadGrantMsg()); // ijinkan upload
        
        do {
            mtype = in.read();
            System.out.println("type : " + mtype);
            if(mtype == GlobalSettings.END_UPLOAD_FILE) {
                //client sudah mengirim file utuh
                System.out.println("menerima END_UPLOAD_FILE");
                break;                    
            }else if(mtype == -1) {
                break;
            }else if(mtype == GlobalSettings.UPLOAD_FILEPIECE) {
                //tunggu kiriman piece
                byte[] piece = listenFilePiece(ipiece);
                if(piece != null) {
                    System.out.println("menerima piece sepanjang : " + piece.length);
                    FileUtil.appendFile(userInfo, filepath, filename + ".TEMP", piece);
                    //kirim ACK
                    sendMsg(out, Message.createACKPiece(ipiece));
                    System.out.println("mengirimkan ACK " + ipiece);
                    ipiece++;
                }else{
                    System.out.println("nomor piece yang diterima salah");
                }
            }                                    
        }while(true);        
        
        //delete file dengan nama filename jika ada, dan ubah file temp namanya menjadi file asli
        File oldFile = FileUtil.getFile(userInfo,filepath, filename);
        if(oldFile!=null && oldFile.exists())
            oldFile.delete();
        File newFile = FileUtil.getFile(userInfo, filepath, filename + ".TEMP");
        boolean b = newFile.renameTo(oldFile);
        System.out.println("hasil : " + b);
    }        
    
    protected byte[] listenFilePiece(int k) throws IOException {
        byte[] bmsg = new byte[GlobalSettings.PIECE_LENGTH + 1000];        
        int bmsglen = in.read(bmsg);        
        ByteBuffer bb = ByteBuffer.allocate(4);        
        bb.put(bmsg, 0, 4);                
        int ipiece = bb.getInt(0);                         
        
        
        if(ipiece == k) {
            byte[] piece = new byte[bmsglen - 4];
            System.arraycopy(bmsg, 4, piece, 0, bmsglen - 4);
            return piece;
        }
        
        return null;
    }
    
    protected void replyMetaLogFile() throws FileNotFoundException, IOException {
        File logFile = LogFile.createLogFile(userInfo);        
        sendMsg(out, Message.createMetaLogFile(userInfo)); // kirim metalogfile
        int mtype;
        String logfilePieceRequestMsg;
        int ipiece;
        
        RandomAccessFile raf = new RandomAccessFile(logFile,"r");
        
        do {
            //listen pesan berikutnya
            mtype = in.read();
            System.out.println("mtype : " + mtype);
            if(mtype == GlobalSettings.END_REQUEST_LOGFILEPIECE) { 
                System.out.println("menerima pesan END_REQUEST_LOGFILEPIECE");
                break;
            }else if(mtype == -1) {
                break;
            }else if(mtype == GlobalSettings.REQUEST_LOGFILEPIECE) {
                logfilePieceRequestMsg = listenRequestLogFilePiece();                
                ipiece = Integer.valueOf(logfilePieceRequestMsg);
                // kirim piece ke-ipiece                     
                long remaining = logFile.length() - (ipiece * GlobalSettings.PIECE_LENGTH);
                int piecelen = (int) ((remaining > GlobalSettings.PIECE_LENGTH) ? GlobalSettings.PIECE_LENGTH : remaining); 
                byte[] piece = new byte[piecelen];                                        
                raf.readFully(piece);                    
                sendMsg(out, Message.createFilePieceMsg(ipiece, piece));   
                System.out.println("selesai mengirimkan piece ke-" + ipiece);
            }
            
        }while(true);                
    }
    
    protected String listenRequestLogFilePiece() throws IOException {
        byte[] bmsg = new byte[GlobalSettings.PIECE_LENGTH];        
        int bmsglen = in.read(bmsg);
        String message = new String(bmsg, 0, bmsglen);        
        message = message.substring(0, bmsglen);      
        System.out.println("menerima pesan request LOGFILE piece : " + message);
        return message;
    }
    
    public void run() {
        //cek username dan password
        try {            
            boolean loggedIn = listenUsrPwd();
            if(!loggedIn) {
                //kirimkan pesan gagal login, kemudian close koneksi
                sendMsg(out, Message.createErrLoginMsg());
                socket.close();
            }else{
                //kirimkan pesan sukses login
                sendMsg(out, Message.createSuccessLoginMsg());
            }
            
            while(!close) {
                listenMsg();                
            }
            dbcon.close();
        } catch (Exception ex) {
            Logger.getLogger(ConnectionHandler.class.getName()).log(Level.SEVERE, null, ex);
        }        
    }
    
    protected void startDBConnection() throws SQLException {
        dbcon = DriverManager.getConnection(GlobalSettings.DB_URL);        
    }
    
    protected void stopDBConnection() throws SQLException {
        dbcon.close();
    }       
        
    protected void sendMsg(BufferedOutputStream out, byte[] msg) throws IOException {
        //System.out.println("mengirimkan " + new String(msg));
        out.write(msg);
        out.flush();
    }
    
    public static void main(String[] args) {        
        
    }
}
